#include "xvideowidget.h"

#define GET_STR(x) #x
#define A_VER 3
#define T_VER 4
FILE *fp = NULL;
// 顶点着色器
const char *vString = GET_STR(
    attribute vec4 vertexIn;
    attribute vec2 textureIn;
    varying vec2 textureOut;
    void main(void)
    {
        gl_Position = vertexIn;
        textureOut = textureIn;
    }
);


//片元着色器
const char *tString = GET_STR(
    varying vec2 textureOut;
    uniform sampler2D tex_y;
    uniform sampler2D tex_u;
    uniform sampler2D tex_v;
    void main(void)
    {
        vec3 yuv;
        vec3 rgb;
        yuv.x = texture2D(tex_y, textureOut).r;
        yuv.y = texture2D(tex_u, textureOut).r - 0.5;
        yuv.z = texture2D(tex_v, textureOut).r - 0.5;
        rgb = mat3(1.0, 1.0, 1.0,
            0.0, -0.39465, 2.03211,
            1.13983, -0.58060, 0.0) * yuv;
        gl_FragColor = vec4(rgb, 1.0);
    }

);

// 准备 yuv 数据
// ffmpeg -i v1080.mp4 -t 10 -s 240x128 -pix_fmt yuv420p out240x128.yuv
XVideoWidget::XVideoWidget(QWidget *parent) : QOpenGLWidget(parent)
{

}

// 初始化 opengl
void XVideoWidget::initializeGL()
{
    qDebug() << __func__ << "\n";

    // 初始化 opengl (QOpenGLFunctions 继承) 函数
    initializeOpenGLFunctions();

    // program 加载 shader （顶点和片元） 脚本
    // 片元 (像素 着色)
    qDebug() << "片元着色器 绑定代码: " << program.addShaderFromSourceCode(QOpenGLShader::ShaderTypeBit::Fragment, tString);
    qDebug() << "顶点 shader 绑定代码: " << program.addShaderFromSourceCode(QOpenGLShader::ShaderTypeBit::Vertex, vString);

    // 设置顶点坐标的变量
    program.bindAttributeLocation("vertexIn", A_VER);
    // 设置材质坐标
    program.bindAttributeLocation("textureIn", T_VER);

    // 编译 shader
    qDebug() << "编译 shader: " << program.link();
    qDebug() << "绑定 shader: " << program.bind();

    // 传递顶点和材质坐标
    // 顶点
    static const GLfloat ver[] = {
        -1.0f, -1.0f,
         1.0f, -1.0f,
        -1.0f,  1.0f,
         1.0f,  1.0f
    };

    // 材质
    static const GLfloat tex[] = {
        0.0f, 1.0f,
        1.0f, 1.0f,
        0.0f, 0.0f,
        1.0f, 0.0f
    };

    // 顶点
    glVertexAttribPointer(A_VER, 2, GL_FLOAT, 0, 0, ver);
    glEnableVertexAttribArray(A_VER);

    // 材质
    glVertexAttribPointer(T_VER, 2, GL_FLOAT, 0, 0, tex);
    glEnableVertexAttribArray(T_VER);

    // 从 shader 获取材质
    unis[0] = program.uniformLocation("tex_y");
    unis[1] = program.uniformLocation("tex_u");
    unis[2] = program.uniformLocation("tex_v");

    // 创建材质  应该是在 显存中创建空间
    glGenTextures(3, texs);

    // Y
    glBindTexture(GL_TEXTURE_2D, texs[0]);
    // 放大过滤， 线性插值  GL_NEAREST(效率高， 但马赛克严重)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    // 创建材质显卡空间
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, 0);

    // U
    glBindTexture(GL_TEXTURE_2D, texs[1]);
    // 放大过滤， 线性插值  GL_NEAREST(效率高， 但马赛克严重)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    // 创建材质显卡空间
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width/2, height/2, 0, GL_RED, GL_UNSIGNED_BYTE, 0);

    // V
    glBindTexture(GL_TEXTURE_2D, texs[2]);
    // 放大过滤， 线性插值  GL_NEAREST(效率高， 但马赛克严重)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    // 创建材质显卡空间
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width/2, height/2, 0, GL_RED, GL_UNSIGNED_BYTE, 0);

    // 分配材质内存空间
    datas[0] = new unsigned char[width * height]; // Y
    datas[1] = new unsigned char[width * height / 4]; // U
    datas[2] = new unsigned char[width * height / 4]; // V

    fp = fopen("/home/leo/qt-work/opengl/display_yuv_video/out240x128.yuv", "rb");
    if (!fp) {
        qDebug() << "out240x128.yuv file open failed!";
    }

    // 启动定时器
    QTimer *ti = new QTimer(this);
    connect(ti, SIGNAL(timeout()), this, SLOT(update()));
    ti->start(40);


}

void XVideoWidget::resizeGL(int w, int h)
{
    qDebug() << __func__;
}

// 刷新显示
void XVideoWidget::paintGL()
{
    if (feof(fp)) {
        fseek(fp, 0, SEEK_SET);
    }
    fread(datas[0], 1, width*height, fp);
    fread(datas[1], 1, width*height/4, fp);
    fread(datas[2], 1, width*height/4, fp);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texs[0]); // 0 层绑定到Y材质
    // 修改材质内容 (复制内存内容)
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RED, GL_UNSIGNED_BYTE, datas[0]);
    // 与 shader uni 遍历关联
    glUniform1i(unis[0], 0);

    glActiveTexture(GL_TEXTURE0 +1);
    glBindTexture(GL_TEXTURE_2D, texs[1]); // 1 层绑定到U材质
    // 修改材质内容 (复制内存内容)
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width/2, height/2, GL_RED, GL_UNSIGNED_BYTE, datas[1]);
    // 与 shader uni 遍历关联
    glUniform1i(unis[1], 1);

    glActiveTexture(GL_TEXTURE0 +2);
    glBindTexture(GL_TEXTURE_2D, texs[2]); // 2 层绑定到V材质
    // 修改材质内容 (复制内存内容)
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width/2, height/2, GL_RED, GL_UNSIGNED_BYTE, datas[2]);
    // 与 shader uni 遍历关联
    glUniform1i(unis[2], 2);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    qDebug() << __func__;
}
